package org.ala.tiktools.sharddb.tx;

import static org.springframework.util.Assert.notNull;

import org.ala.tiktools.logs.LoggerFactory;
import org.ala.tiktools.mybatis.spring.TikToolsMyBatisSpringContext;
import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionHolder;
import org.slf4j.Logger;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;

/**
 * 替换原 SqlSessionSynchronization
 * 
 * @author ala
 * @date 2025年5月9日
 */
public class ShardSqlSessionSynchronization extends TransactionSynchronizationAdapter {
	static Logger log = LoggerFactory.getLogger("shard", "transaction");
	
	
	/**
	 * 事务环境
	 */
	protected ShardTransactionContext context;

	private final SqlSessionHolder holder;

	private final SqlSessionFactory sessionFactory;

	private boolean holderActive = true;

	public ShardSqlSessionSynchronization(ShardTransactionContext context) {
		this.context = context;
		
		this.sessionFactory = TikToolsMyBatisSpringContext.getBean(SqlSessionFactory.class);
		this.holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

		notNull(holder, "Parameter 'holder' must be not null");
		notNull(sessionFactory, "Parameter 'sessionFactory' must be not null");
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getOrder() {
		return DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 1;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void suspend() {
		if (this.holderActive) {
			if (log.isDebugEnabled()) {
				log.debug("Transaction synchronization suspending SqlSession [" + this.holder.getSqlSession() + "]");
			}
//			TransactionSynchronizationManager.unbindResource(this.sessionFactory);
			context.unbindResource(this.sessionFactory);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void resume() {
		if (this.holderActive) {
			if (log.isDebugEnabled()) {
				log.debug("Transaction synchronization resuming SqlSession [" + this.holder.getSqlSession() + "]");
			}
//			TransactionSynchronizationManager.bindResource(this.sessionFactory, this.holder);
			context.bindResource(this.sessionFactory, this.holder);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void beforeCommit(boolean readOnly) {
		if (context.isActualTransactionActive()) {
			try {
				if (log.isDebugEnabled()) {
					log.debug("Transaction synchronization committing SqlSession [" + this.holder.getSqlSession() + "]");
				}
				this.holder.getSqlSession().commit();
			} catch (PersistenceException p) {
				if (this.holder.getPersistenceExceptionTranslator() != null) {
					DataAccessException translated = this.holder.getPersistenceExceptionTranslator().translateExceptionIfPossible(p);
					if (translated != null) {
						throw translated;
					}
				}
				throw p;
			}
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void beforeCompletion() {
		// Issue #18 Close SqlSession and deregister it now
		// because afterCompletion may be called from a different thread
		if (!this.holder.isOpen()) {
			if (log.isDebugEnabled()) {
				log.debug("Transaction synchronization deregistering SqlSession [" + this.holder.getSqlSession() + "]");
			}
//			TransactionSynchronizationManager.unbindResource(sessionFactory);
			context.unbindResource(sessionFactory);
			this.holderActive = false;
			if (log.isDebugEnabled()) {
				log.debug("Transaction synchronization closing SqlSession [" + this.holder.getSqlSession() + "]");
			}
			//	这里不执行关闭，留给下面 afterCompletion 执行
//			this.holder.getSqlSession().close();
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void afterCompletion(int status) {
		if (this.holderActive) {
			// afterCompletion may have been called from a different thread
			// so avoid failing if there is nothing in this one
			if (log.isDebugEnabled()) {
				log.debug("Transaction synchronization deregistering SqlSession [" + this.holder.getSqlSession() + "]");
			}
//			TransactionSynchronizationManager.unbindResourceIfPossible(sessionFactory);
			context.unbindResource(sessionFactory);
			this.holderActive = false;
			if (log.isDebugEnabled()) {
				log.debug("Transaction synchronization closing SqlSession [" + this.holder.getSqlSession() + "]");
			}
			this.holder.getSqlSession().close();
		}
		this.holder.reset();
	}

}
